package com.lazy.book.service.system.impl;

import com.lazy.book.common.cache.RedisCache;
import com.lazy.book.common.entity.DictVO;
import com.lazy.book.common.entity.PageResult;
import com.lazy.book.common.enums.CommonResponseEnum;
import com.lazy.book.common.utils.BeanCopyUtils;
import com.lazy.book.common.utils.PageUtils;
import com.lazy.book.common.utils.Utils;
import com.lazy.book.pojo.entity.SelectIdsDTO;
import com.lazy.book.service.generator.GeneratorTableService;
import com.lazy.book.service.system.SysDictService;
import com.lazy.book.service.system.SysDictTypeService;
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.lazy.book.mapper.system.SysDictMapper;
import com.lazy.book.mapper.system.SysDictTypeMapper;
import com.lazy.book.pojo.system.pojo.dto.sysdict.SysDictCreateDTO;
import com.lazy.book.pojo.system.pojo.dto.sysdict.SysDictListDTO;
import com.lazy.book.pojo.system.pojo.dto.sysdict.SysDictUpdateDTO;
import com.lazy.book.pojo.system.pojo.po.SysDict;
import com.lazy.book.pojo.system.pojo.po.SysDictType;

import freemarker.template.Template;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.StringWriter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.lazy.book.pojo.system.pojo.po.table.SysDictTypeTableDef.SYS_DICT_TYPE;

/**
 * @author lay
 * @since 2025-09-05
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> implements SysDictService, DictService {

  private final SysDictTypeMapper sysDictTypeMapper;

  private final SysDictTypeService sysDictTypeService;

  private final RedisCache redisCache;

  private final GeneratorTableService generatorTableService;

  private final DictLoaderFactory dictLoaderFactory;

  private static Long generateCustomId(Long firstPart, int secondPart) {
    secondPart += 1;
    String paddedFirstPart = String.format("%04d", firstPart); // 格式化为四位数字，不足补零
    String paddedSecondPart = String.format("%03d", secondPart); // 格式化为三位数字，不足补零
    String part = paddedFirstPart + paddedSecondPart;
    return Long.valueOf(part);
  }

  @Override
  public void create(SysDictCreateDTO dto) {
    SysDict sysDict = BeanCopyUtils.copy(dto, SysDict.class);
    AtomicReference<Long> id = new AtomicReference<>();
    LogicDeleteManager.execWithoutLogicDelete(() -> {
      id.set(QueryChain.of(SysDict.class).select(SysDictTableDef.SYS_DICT.ID.max()).where(SysDictTableDef.SYS_DICT.TYPE_ID.eq(dto.getTypeId()))
          .limit(1).oneAsType(Long.class));
    });
    if (id.get() == null) {
      id.set(dto.getTypeId() * 1000L);
    } else {
      id.set(generateCustomId(dto.getTypeId(), Math.toIntExact(id.get() % 1000)));
    }
    sysDict.setId(id.get());
    save(sysDict);
    // 更新缓存
    SysDictType dictType = sysDictTypeMapper.selectOneById(dto.getTypeId());
    redisCache.setCacheMapValue(dictType.getTypeCode(), sysDict.getDictValue(), sysDict.getDictLabel());
  }

  @Override
  public void update(SysDictUpdateDTO dto) {
    SysDict sysDict = BeanCopyUtils.copy(dto, SysDict.class);
    updateById(sysDict);
    // 更新缓存
    SysDictType dictType = sysDictTypeMapper.selectOneById(dto.getTypeId());
    redisCache.setCacheMapValue(dictType.getTypeCode(), sysDict.getDictValue(), sysDict.getDictLabel());
  }

  private void upCache(Long dictTypeId) {
    SysDictType sysDictType = sysDictTypeService.detail(dictTypeId);
    String typeCode = sysDictType.getTypeCode();
    redisCache.clearDict(typeCode); // 清除redis缓存
    dictLoaderFactory.getDictByType(typeCode); // 更新缓存
  }

  @Override
  public void remove(SelectIdsDTO dto) {
    CommonResponseEnum.INVALID_ID.assertIsFalse(dto.getIds().isEmpty());
    List<SysDict> sysDicts = listByIds(dto.getIds());
    removeByIds(dto.getIds());
    // 更新缓存
    sysDicts.forEach(sysDict -> {
      SysDictType dictType = sysDictTypeMapper.selectOneById(sysDict.getTypeId());
      redisCache.delCacheMapValue(dictType.getTypeCode(), sysDict.getDictValue());
    });
  }

  @Override
  public PageResult<SysDict> page(SysDictListDTO dto) {
    QueryWrapper wrapper = QueryWrapper.create().from(SysDictTableDef.SYS_DICT);
    if (Utils.isNotBlank(dto.getTypeCode())) {
      wrapper.and(SysDictTableDef.SYS_DICT.TYPE_CODE.like(dto.getTypeCode()));
    }
    if (Utils.isNotBlank(dto.getDictLabel())) {
      wrapper.and(SysDictTableDef.SYS_DICT.DICT_LABEL.like(dto.getDictLabel()));
    }
    Page<SysDict> page = page(dto.buildPage(), wrapper);
    return PageUtils.getPageResult(page);
  }

  @Override
  public List<SysDict> list(SysDictListDTO dto) {
    QueryWrapper wrapper = QueryWrapper.create().from(SysDictTableDef.SYS_DICT);
    if (Utils.isNotBlank(dto.getTypeCode())) {
      wrapper.and(SysDictTableDef.SYS_DICT.TYPE_CODE.like(dto.getTypeCode()));
    }
    if (Utils.isNotBlank(dto.getDictLabel())) {
      wrapper.and(SysDictTableDef.SYS_DICT.DICT_LABEL.like(dto.getDictLabel()));
    }
    return list(wrapper);
  }

  @Override
  public SysDict detail(Object id) {
    return getById(id);
  }

  @Override
  public List<Map<String, Object>> getDictDataByTypeCode(String typeCode) {
    Map<String, String> cacheMap = redisCache.getCacheMap(typeCode);
    if (cacheMap == null || cacheMap.isEmpty()) {
      QueryWrapper wrapper = QueryWrapper.create().from(SysDictTableDef.SYS_DICT).where(SysDictTableDef.SYS_DICT.TYPE_CODE.eq(typeCode));
      List<SysDict> sysDicts = list(wrapper);
      Map<String, String> map = new LinkedHashMap<>();
      sysDicts.forEach(sysDict -> map.put(sysDict.getDictValue(), sysDict.getDictLabel()));
      redisCache.setCacheMap(typeCode, map);
      return sysDicts.stream().map(sysDict -> {
        Map<String, Object> result = new HashMap<>();
        result.put("label", sysDict.getDictLabel());
        result.put("value", sysDict.getDictValue());
        return result;
      }).collect(Collectors.toList());
    } else {
      return cacheMap.entrySet().stream().map(entry -> {
        Map<String, Object> result = new HashMap<>();
        result.put("label", entry.getValue());
        result.put("value", entry.getKey());
        return result;
      }).collect(Collectors.toList());
    }
  }

  @Override
  public Set<String> getDictTypeByTypeName(String typeName) {
    return null;
  }

  @SneakyThrows
  @Override
  public void loadingDictToFreemarker(Template template, StringWriter writer, Map<String, Object> dataModel) {
    List<SysDictType> dictTypes = sysDictTypeService.list();
    Map<String, List<DictVO>> dictMap = new HashMap<>();
    dictTypes.forEach(dictType -> {
      List<Map<String, Object>> dictData = getDictDataByTypeCode(dictType.getTypeCode());
      List<DictVO> dictVOList = dictData.stream().map(dict -> {
        DictVO dictVO = new DictVO();
        dictVO.setLabel(dict.get("label").toString());
        dictVO.setValue(dict.get("value").toString());
        return dictVO;
      }).collect(Collectors.toList());
      dictMap.put(dictType.getTypeCode(), dictVOList);
    });
    dataModel.put("dict", dictMap);
    template.process(dataModel, writer);
  }

  @Override
  public void refreshDictCache() {
    List<SysDictType> dictTypes = sysDictTypeService.list();
    dictTypes.forEach(dictType -> {
      List<Map<String, Object>> dictData = getDictDataByTypeCode(dictType.getTypeCode());
      Map<String, String> map = new LinkedHashMap<>();
      dictData.forEach(dict -> map.put(dict.get("value").toString(), dict.get("label").toString()));
      redisCache.setCacheMap(dictType.getTypeCode(), map);
    });
  }

  @Override
  public List<EnumerationCustomizeEnum> getEnumsByTypeCode(String typeCode) {
    return dictLoaderFactory.getEnumsByTypeCode(typeCode);
  }

  @Override
  public Map<String, List<EnumerationCustomizeEnum>> getAllEnums() {
    return dictLoaderFactory.getAllEnums();
  }

  @SneakyThrows
  @Override
  public String exportDictSql(SelectIdsDTO dto) {
    String generatedContent = "";
    if (Utils.isNotNull(dto.getIds())) {
      List<SysDictType> dictTypeList = QueryChain.of(SysDictType.class).where(SYS_DICT_TYPE.ID.in(dto.getIds())).list();

      QueryWrapper queryWrapper = QueryWrapper.create().in(SysDict::getSysDictTypeId, dto.getIds()).orderBy(SysDict::getSysDictTypeId).asc()
          .orderBy(SysDict::getSort).asc();
      List<SysDict> dictList = list(queryWrapper);
      Map<String, Object> dataModel = new HashMap<>();
      dataModel.put("dictTypeList", dictTypeList);
      dataModel.put("dictList", dictList);
      Template template = generatorTableService.getDictSqlTemplate();
      try (StringWriter writer = new StringWriter()) {
        template.process(dataModel, writer);
        generatedContent = writer.toString();
      } catch (Exception e) {
        log.error("exportDictSql err", e);
      }

    }
    return generatedContent;
  }

  @Override
  public Map<String, List<DictVO>> getDictByCode(List<String> typeCode) {
    return typeCode.stream().collect(Collectors.toMap(code -> code, this::getDictByType));
  }

  public String getSort(String dictType, String dictValue) {
    return String.valueOf(dictLoaderFactory.getDictByType(dictType).stream().filter(vo -> vo.getId().equals(dictValue)).findFirst().map(DictVO::getSort).get() - 1);

  }

  public String getDictValueByCustomize(String enumeration, String s, EnumerationCustomizeEnum customizeEnum) {
    return switch (customizeEnum.getType()) {
      case "sort" -> getSort(enumeration, s);
      case "alias" -> getDictAlias(enumeration, s);
      case "value" -> getDictValue(enumeration, s);
      default -> getDictLabel(enumeration, s);
    };
  }

  private String getDictAlias(String enumeration, String s) {
    return dictLoaderFactory.getDictByType(enumeration).stream().filter(vo -> vo.getId().equals(s)).findFirst().map(DictVO::getAlias).get();
  }
}
